Understanding WPF’s Animation Services

In addition to the graphical rendering services you examined in Chapter 29, WPF supplies a programming interface to support animation services. The term animation may bring to mind visions of spinning company logos, a sequence of rotating image resources (to provide the illusion of movement), text bouncing across the screen, or specific types of programs such as video games or multimedia applications.

While WPF’s animation APIs could certainly be used for such purposes, animation can be used any time you wish to give an application additional flair. For example, you could build an animation for a button on a screen that magnifies slightly when the mouse cursor hovers within its boundaries (and shrinks back once the mouse cursor moves beyond the boundaries). Or you could animate a window so that it closes using a particular visual appearance, such as slowly fading into transparency. In fact, WPF’s animation support can be used within any sort of application (a business application, multimedia programs, video games, etc.) whenever you wish to provide a more engaging user experience.

As with many other aspects of WPF, the notion of building animations is nothing new. What is new is that, unlike other APIs you may have used in the past (including Windows Forms), developers are not required to author the necessary infrastructure by hand. Under WPF, there’s no need to create the background threads or timers used to advance the animation sequence, define custom types to represent the animation, erase and redraw images, or bother with tedious mathematical calculations.

Like other aspects of WPF, we can build an animation entirely using XAML, entirely using C# code, or using a combination of the two. Furthermore, we can use Microsoft Expression Blend to design an animation using integrated tools and wizards—without seeing a bit of C# or XAML in the foreground. You will see how to use Blend to author animations in the next chapter during our discussion of control templates. For now, let’s get to know the overall role of the animation API.

Note Visual Studio 2010 has no support for authoring animations using GUI animation tools. If you author an animation with Visual Studio, you will do so by typing in the XAML directly.

The Role of the Animation Class Types

To understand WPF’s animation support, we must begin by examining the animation classes within the System.Windows.Media.Animation namespace of PresentationCore.dll. Here you will find over 100 different class types that are named using the Animation token.

All of these classes can be placed into one of three broad categories. First of all, any class that follows the name convention DataTypeAnimation (ByteAnimation, ColorAnimation, DoubleAnimation, In32Animation, etc.) allows you to work with linear interpolation animations. This enables you to change a value smoothly over time from a start value to a final value.

Next, the classes that follow the naming convention DataTypeAnimationUsingKeyFrames (StringAnimationUsingKeyFrames, DoubleAnimationUsingKeyFrames, PointAnimationUsingKeyFrames, etc.) represent “key frame animations,” which allow you to cycle through a set of defined values over a period of time. For example, you could use keyframes to change the caption of a button by cycling through a series of individual characters.

Finally, classes that follow the DataTypeAnimationUsingPath naming convention (DoubleAnimationUsingPath, PointAnimationUsingPath, among others) are path-based animations that allow you to animate objects to move along a path you define. By way of an example, if you were building a GPS application, you could use a path-based animation to move an item along the quickest travel route to the user’s destination.

Now, obviously, these classes are not used to somehow provide an animation sequence directly to a variable of a particular data type (after all, how exactly could we animate the value “9” using an Int32Animation?).

For example, consider the Label type’s Height and Width properties, both of which are dependency properties wrapping a double. If you wish to define an animation that would increase the height of a label over a time span, you could connect a DoubleAnimation object to the Height property and allow WPF to take care of the details of performing the actual animation itself. By way of another example, if you wish to transition the color of a brush type from green to yellow over a period of 5 seconds, you could do so using the ColorAnimation type.

To be very clear, these Animation classes can be connected to any dependency property of a given object that matches the underlying types. As explained in Chapter 31, dependency properties are a specialized form of property required by many WPF services including animation, data binding, and styles.

By convention, a dependency property is defined as a static read-only field of the class, and is named by suffixing the word Property to the normal property name. For example, the dependency property for the Height property of a Button would be accessed in code using Button.HeightProperty.

The To, From, and By Properties

All Animation classes define a handful of key properties that control the starting and ending values used to perform the animation:

Despite the fact that all Animation classes support the To, From, and By properties, they do not receive them via virtual members of a base class. The reason for this is that the underlying types wrapped by these properties vary greatly (integers, colors, Thickness objects, etc.), and representing all possibilities using a single base class would result in very complex coding constructs.

On a related note, you might also wonder why .NET generics were not used to define a single generic animation class with a single type parameter (e.g., Animate<T>). Again, given that there are so many underlying data types (colors, vectors, ints, strings, etc.) used to animated dependency properties, it would not be as clean a solution as you might expect (not to mention XAML has only limited support for generic types).

The Role of the Timeline Base Class

Although a single base class was not used to define virtual To, From, and By properties, the Animation classes do share a common base class: System.Windows.Media.Animation.Timeline. This type provides a number of additional properties that control the pacing of the animation, as described in Table 30-1.

Table 30-1. Key Members of the Timeline Base Class

Properties Meaning in Life
AccelerationRatio, DecelerationRatio, SpeedRatio These properties can be used to control the overall pacing of the animation sequence.
AutoReverse This property gets or sets a value that indicates whether the timeline plays in reverse after it completes a forward iteration (the default value is false).
BeginTime This property gets or sets the time at which this timeline should begin. The default value is 0, which begins the animation immediately.
Duration This property allows you to set a duration of time to play the timeline.
FillBehavior, RepeatBehavior These properties are used to control what should happen once the timeline has completed (repeat the animation, do nothing, etc.).

Authoring an Animation in C# Code

The chances are quite good that a majority of your WPF animation projects will be authored using the Expression Blend animation editor, and will therefore be represented as markup. However, our first look at WPF’s animation services will use nothing but C# code, as I feel it is a tad more straightforward.

Specifically, we will build a Window that contains a Button, which has the odd behavior of spinning in a circle (based on the upper left corner) whenever the mouse enters its surface area. Begin by creating a new WPF application named SpinningButtonAnimationApp, using Visual Studio 2010. Update the initial markup like the following (note we are handling the button’s MouseEnter event):

public partial class MainWindow : Window
{
    private bool isSpinning = false;

    private void btnSpinner_MouseEnter(object sender, MouseEventArgs e)
    {
        if (!isSpinning)
        {
            isSpinning = true;

            // Make a double animation object, and register
            // with the Completed event.
            DoubleAnimation dblAnim = new DoubleAnimation();
            dblAnim.Completed += (o, s) => { isSpinning = false; };

            // Set the start value and end value.
            dblAnim.From = 0;
            dblAnim.To = 360;

            // Now, create a RotateTransform object, and set
            // it to the RenderTransform property of our
            // button.
            RotateTransform rt = new RotateTransform();
            btnSpinner.RenderTransform = rt;

            // Now, animation the RotateTransform object.
            rt.BeginAnimation(RotateTransform.AngleProperty, dblAnim);
        }
    }
}

The first major task of this method is to configure a DoubleAnimation object, which will start at the value 0 and end at the value 360. Notice that we are handling the Completed event on this object as well, to toggle a class-level bool variable that is used to ensure that if an animation is currently being performed, we don’t “reset” it to start again.

Next, we create a RotateTransform object that is connected to the RenderTransform property of our Button control (btnSpinner). Last but not least, we inform the RenderTransform object to begin animating its Angle property using our DoubleAnimation object. When you are authoring animations in code, you typically do so by calling BeginAnimation(), and pass in the underlying dependency property you wish to animate (remember, by convention, this is a static field on the class), followed by a related animation object.

Let’s add another animation to the program, which will cause the button to fade into invisibility when clicked. First, handle the Click event of the btnSpinner object, then add the following code in the resulting event handler:

private void btnSpinner_Click(object sender, RoutedEventArgs e)
{
    DoubleAnimation dblAnim = new DoubleAnimation();
    dblAnim.From = 1.0;
    dblAnim.To = 0.0;
    btnSpinner.BeginAnimation(Button.OpacityProperty, dblAnim);
}

Here, we are changing the Opacity property value to fade the button out of view. Currently, however, this is hard to do, as the button is spinning very fast! How, then, can we control the pace of an animation? Glad you asked.

Controlling the Pacing of an Animation

By default, an animation will take approximately one second to transition between the values assigned to the From and To properties. Therefore, our button has one second to spin around a full 360 degree angle, while the button will fade away to invisibility (when clicked) over the course of one second.

If you wish to define a custom amount of time for an animation’s transition, you may do so via the animation object’s Duration property, which can be set to an instance of a Duration object. Typically, the time span is established by passing a TimeSpan object to the Duration’s constructor. Consider the following update that will give the button a full 4 seconds to rotate:

private void btnSpinner_MouseEnter(object sender, MouseEventArgs e)
{
    if (!isSpinning)
    {
        isSpinning = true;

        // Make a double animation object, and register
        // with the Completed event.
        DoubleAnimation dblAnim = new DoubleAnimation();
        dblAnim.Completed += (o, s) => { isSpinning = false; };

        // Button has 4 seconds to finish the spin!
        dblAnim.Duration = new Duration(TimeSpan.FromSeconds(4));
...
    }
}

With this adjustment, you should have a fighting chance to click the button while it is spinning, at which point it will fade away.

Note The BeginTime property of an Animation class also takes a TimeSpan object. Recall that this property can be set to establish a wait time before starting an animation sequence.

Reversing and Looping an Animation

You can also tell Animation objects to play an animation in reverse at the completion of the animation sequence by setting the AutoReverse property to true. For example, if you wish to have the button come back into view after it has faded away, you could author the following:

private void btnSpinner_Click(object sender, RoutedEventArgs e)
{
    DoubleAnimation dblAnim = new DoubleAnimation();
    dblAnim.From = 1.0;
    dblAnim.To = 0.0;

    // Reverse when done.
    dblAnim.AutoReverse = true;
    btnSpinner.BeginAnimation(Button.OpacityProperty, dblAnim);
}

If you’d like to have an animation repeat some number of times (or to never stop once activated), you can do so using the RepeatBehavior property, which is common to all Animation classes. If you pass in a simple numerical value to the constructor, you can specify a hard-coded number of times to repeat.

On the other hand, if you pass in a TimeSpan object to the constructor, you can establish an amount of time the animation should repeat. Finally, if you wish an animation to loop ad infinitum, you can simply specify RepeatBehavior.Forever. Consider the following ways we could change the repeat behaviors of either of the DoubleAnimation objects used in this example:

// Loop forever.
dblAnim.RepeatBehavior = RepeatBehavior.Forever;

// Loop three times.
dblAnim.RepeatBehavior = new RepeatBehavior(3);

// Loop for 30 seconds.
dblAnim.RepeatBehavior = new RepeatBehavior(TimeSpan.FromSeconds(30));

That wraps up our investigation about how to animate aspects of an object using C# code and the WPF animation API. Next, we will learn how to do the same using XAML.